package com.hbisedm.interninfosys.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hbisedm.interninfosys.commonutils.R;
import com.hbisedm.interninfosys.exception.JWTAccessDeniedHandler;
import com.hbisedm.interninfosys.exception.JWTAuthenticationEntryPoint;
import com.hbisedm.interninfosys.filter.JWTAuthenticationFilter;
import com.hbisedm.interninfosys.filter.JWTAuthorizationFilter;
import com.hbisedm.interninfosys.intern.entity.LoginUser;
import com.hbisedm.interninfosys.intern.entity.Role;
import com.hbisedm.interninfosys.intern.entity.User;
import com.hbisedm.interninfosys.intern.mapper.RoleMapper;
import com.hbisedm.interninfosys.intern.service.IRoleService;
import com.hbisedm.interninfosys.intern.service.IUserService;
import com.hbisedm.interninfosys.intern.service.impl.RoleServiceImpl;
import com.hbisedm.interninfosys.utils.JwtTokenUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    RoleServiceImpl roleService;
    @Autowired
    IUserService userService;
    @Bean
    JWTAuthenticationFilter loginFilter() throws Exception {
        JWTAuthenticationFilter loginFilter = new JWTAuthenticationFilter();
        //成功回调
        loginFilter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                LoginUser principal = (LoginUser) authentication.getPrincipal();
                List<Role> roles = roleService.getBaseMapper().getRolesByUid(principal.getUsername());
//                String role = JwtTokenUtils.splitRolesList(roles);
                response.setContentType("application/json;charset=utf-8");
                PrintWriter out = response.getWriter();
                /**
                 * 每个用户对应每个角色，（暂时）
                 * 取角色列表中第一个角色，当作当前用户的角色，
                 */
                String token = JwtTokenUtils.createToken(authentication.getName(), "ROLE_"+roles.get(0).getRoleName(), false);
                JSONObject jsonObject = new JSONObject();
//                jsonObject.put("sessionId", httpServletRequest.getSession().getId());
//                jsonObject.put("user", authentication.getPrincipal());
                QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
                userQueryWrapper.eq("USERNAME", principal.getUsername());
                User one = userService.getOne(userQueryWrapper);
                HashMap<String, Object> hashMap = new HashMap<>();
                hashMap.put("token", token);
                R r = R.ok()
                        .data("token", token)
                        //当前用户名称
                        .data("username", principal.getUsername())
                        //当前用户id
                        .data("id", one.getId());
                hashMap.put("code", 20000);
                jsonObject.put("data", hashMap);
                String s = JSON.toJSONString(r);
                out.write(s);
                out.flush();
                out.close();
            }
        });
        //失败回调
        loginFilter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
            @Override
            public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                response.setContentType("application/json;charset=utf-8");
                PrintWriter out = response.getWriter();
                Map<Object, String> map = new HashMap<>();
                if (exception instanceof LockedException) {
                    map.put("error","账户被锁定，请联系管理员!");
                } else if (exception instanceof CredentialsExpiredException) {
                    map.put("error","密码过期，请联系管理员!");
                } else if (exception instanceof AccountExpiredException) {
                    map.put("error","账户过期，请联系管理员!");
                } else if (exception instanceof DisabledException) {
                    map.put("error","账户被禁用，请联系管理员!");
                } else if (exception instanceof BadCredentialsException) {
                    map.put("error","用户名或者密码输入错误，请重新输入!");
                }
//                out.write(new ObjectMapper().writeValueAsString(exception.getMessage()));
                out.write(new ObjectMapper().writeValueAsString(map));
                out.flush();
                out.close();
            }
        });

        loginFilter.setAuthenticationManager(authenticationManagerBean());
        loginFilter.setFilterProcessesUrl("/doLogin");
        return loginFilter;
    }

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
        hierarchy.setHierarchy("ROLE_管理员 > ROLE_学校管理员 > ROLE_企业管理员 > ROLE_Banner管理员 > ROLE_普通用户");
        return hierarchy;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
            //防止 a frame because it set 'X-Frame-Options' to 'deny'
            http.headers().frameOptions().disable();

            // 调用 addFilterAt 方法完成替换操作
            http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
            http.cors().and().csrf().disable()
                    .authorizeRequests()
            //.antMatchers(HttpMethod.DELETE, "/test/**").hasRole("普通用户")
                // 测试用资源，需要验证了的用户才能访问

                .antMatchers(
                        "/test/jd-user/register",
                        "/intern/user/register",
                        "/file/**",
                        "/EasypoiSingleExcelViewTest/**",
                        "/intern/stu-internship/makeUnitLetter",
                        "/intern/stu-internship/makeUnitForm",
                        "/intern/user/changeAvatar/**",
                        "/intern/user/changeSignature/**",
                        "/EasypoiSingleExcelViewTest/uploadFile", //上传文件接口
                        "/intern/stu-internship/getUnitLetterStatus").permitAll()

                .antMatchers("/intern/**").hasRole("普通用户")
                // 其他都放行了
                .anyRequest().permitAll()
                .and()
                    .logout()
                    .logoutUrl("/doLogout")
                    .logoutSuccessHandler((request, response, authentication) -> {
                            //成功回调
                        R r = R.ok()
                                .data("logoutStatus", true);
                        String s = JSON.toJSONString(r);
                        response.getWriter().write(s);
                    })
                    .invalidateHttpSession(true)
                    .deleteCookies("Admin-Token")
                    .and()
                .rememberMe().disable()
//                .addFilter(new JWTAuthenticationFilter(authenticationManager()))
                .addFilter(new JWTAuthorizationFilter(authenticationManager()))
                // 不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .exceptionHandling().authenticationEntryPoint(new JWTAuthenticationEntryPoint())
                .accessDeniedHandler(new JWTAccessDeniedHandler());      //添加无权限时的处理
    }

    private static final String[] AUTH_WHITELIST = {
            // -- swagger ui
            "/swagger-resources/**",
            "/swagger-ui.html",
            "/v2/api-docs",
            "/webjars/**"
    };
    @Override
    public void configure(WebSecurity web) throws Exception {
        //web.ignoring().antMatchers(AUTH_WHITELIST); //这样配置会导致 swagger调试接口时候是没有登录的
        web.ignoring().antMatchers(AUTH_WHITELIST);
        web.ignoring().antMatchers("/js/**");
    }


    //配置Cors
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
        return source;
    }

}
